package com.example.opengl.avengine;

import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.opengl.GLES30;
import android.util.Size;
import android.view.Surface;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;

import static android.media.MediaCodec.BUFFER_FLAG_END_OF_STREAM;

/**
 * 视频组件
 */
public class AVVideo extends AVComponent implements IVideo {
    private static final String TAG = AVVideo.class.getSimpleName();
    //    private int textureId;
    private boolean isInputEOF;
    private boolean isOutputEOF;
    private String path;
    private MediaCodec mediaCodec;
    private MediaExtractor mediaExtractor;
    private int gop = 0;
    private MediaFormat mediaFormat;
    private Surface surface;
    private SurfaceTexture surfaceTexture;
    private Size videoSize;
    private boolean isTextureType;
    private int frameRate = 0;
    private AtomicBoolean mIsOk = new AtomicBoolean(false);


    //输出到surface
    public AVVideo(boolean isTextureType, long engineStartTime, String path) {
        super(engineStartTime, AVComponentType.VIDEO);
        this.isTextureType = isTextureType;
        this.path = path;
        this.videoSize = new Size(0, 0);
    }

    @Override
    public String getPath() {
        return path;
    }

    @Override
    public Size getVideoSize() {
        return videoSize;
    }

    @Override
    public int getFrameRate() {
        return frameRate;
    }

    @Override
    public int open() {
        //已经打开 则返回失败
        if (isOpen()) return RESULT_FAILED;
        peekFrame().setValid(false);//TODO release
        mediaExtractor = new MediaExtractor();
        try {
            mediaExtractor.setDataSource(path);
            for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
                if (mediaExtractor.getTrackFormat(i).getString(MediaFormat.KEY_MIME).contains("video")) {
                    mediaFormat = mediaExtractor.getTrackFormat(i);
                    mediaExtractor.selectTrack(i);
                    mediaCodec = MediaCodec.createDecoderByType(mediaExtractor.getTrackFormat(i).getString(MediaFormat.KEY_MIME));
                    frameRate = mediaFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
                    videoSize = new Size(mediaFormat.getInteger(MediaFormat.KEY_WIDTH), mediaFormat.getInteger(MediaFormat.KEY_HEIGHT));
                    long duration = mediaFormat.getLong(MediaFormat.KEY_DURATION);
                    setClipStartTime(0);
                    setClipEndTime(duration);
                    setDuration(duration);

                    break;
                }
            }
            if (mediaCodec == null) {
                return RESULT_FAILED;
            }
            //设置帧缓冲区
            peekFrame().setByteBuffer(ByteBuffer.allocateDirect(mediaFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE)));
            //设置帧图片大小
            peekFrame().setRoi(new Rect(0, 0, mediaFormat.getInteger(MediaFormat.KEY_WIDTH), mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)));
            //如果是纹理类型
            if (isTextureType) {
                //创建纹理
                int[] textures = new int[1];
                //生成纹理
                GLES30.glGenTextures(1, textures, 0);
                surfaceTexture = new SurfaceTexture(textures[0]);
                surface = new Surface(surfaceTexture);
                peekFrame().setTextureId(textures[0]);
                //peekFrame().getTexture().setOes(true);
                peekFrame().setSize(mediaFormat.getInteger(MediaFormat.KEY_WIDTH), mediaFormat.getInteger(MediaFormat.KEY_HEIGHT));
                peekFrame().setSurfaceTexture(surfaceTexture);
                mediaCodec.configure(mediaFormat, surface, null, 0);
            } else {
                mediaCodec.configure(mediaFormat, null, null, 0);
            }
            mediaCodec.start();
            //设置结束时间
            setEngineEndTime(getEngineStartTime() + getDuration());
            markOpen(true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return RESULT_OK;
    }

    @Override
    public int close() {
        if (!isOpen()) return RESULT_FAILED;
        if (mediaCodec != null) {
            mediaCodec.stop();
            mediaCodec.release();
            mediaCodec = null;
        }
        if (mediaExtractor != null) {
            mediaExtractor.release();
            mediaExtractor = null;
        }
        if (surfaceTexture != null) {
            surfaceTexture.setOnFrameAvailableListener(null);
            surfaceTexture.release();
            surfaceTexture = null;
        }
        if (surface != null) {
            surface.release();
            surface = null;
        }
        isInputEOF = false;
        isOutputEOF = false;
        markOpen(false);
        return RESULT_OK;
    }

    @Override
    public int readFrame() {
        if (!isOpen() || isOutputEOF) return RESULT_FAILED;
        while (!isInputEOF || !isOutputEOF) {
            if (!isInputEOF) {
                int inputBufIdx = mediaCodec.dequeueInputBuffer(0);
                if (inputBufIdx >= 0) {
                    int sampleSize = mediaExtractor.readSampleData(peekFrame().getByteBuffer(), 0);
                    if (sampleSize < 0) {
                        sampleSize = 0;
                        isInputEOF = true;
                    }
                    mediaCodec.getInputBuffer(inputBufIdx).put(peekFrame().getByteBuffer());
                    mediaCodec.queueInputBuffer(inputBufIdx, 0,
                            sampleSize,
                            mediaExtractor.getSampleTime(),
                            isInputEOF ? BUFFER_FLAG_END_OF_STREAM : 0);
                    mediaExtractor.advance();

                }
            }
            if (!isOutputEOF) {
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int outputBufIdx = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
                if (outputBufIdx >= 0) {
                    peekFrame().setValid(true);
                    if (bufferInfo.flags == BUFFER_FLAG_END_OF_STREAM) {
                        isOutputEOF = true;
                        peekFrame().setEof(true);//设置结束标志
                        //设置视频帧渲染时间戳
                        peekFrame().setPts(getClipDuration() + getEngineStartTime());
                    } else {
                        peekFrame().setEof(false);
                        peekFrame().setPts(bufferInfo.presentationTimeUs - getClipStartTime() + getEngineStartTime());
                    }
                    peekFrame().setDuration(33333L);//TODO 无效
                    if (!isTextureType) {//no output surface texture
                        ByteBuffer byteBuffer = mediaCodec.getOutputBuffer(outputBufIdx);
                        peekFrame().getByteBuffer().put(byteBuffer);
                        byteBuffer.position(0);
                        peekFrame().getByteBuffer().position(0);
                        mediaCodec.releaseOutputBuffer(outputBufIdx, false);
                    } else {
                        mediaCodec.releaseOutputBuffer(outputBufIdx, true);
                       // surfaceTexture.updateTexImage();
                    }
                    break;
                } else if (outputBufIdx == MediaCodec.INFO_TRY_AGAIN_LATER) {

                } else if (outputBufIdx == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {

                } else if (outputBufIdx == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

                }
            }
        }
        return RESULT_OK;
    }

    @Override
    public int seekFrame(long position) {
        if (!isOpen()) return RESULT_FAILED;

        long correctPosition = position - getEngineStartTime();//engine position => file position
        if (position < getEngineStartTime() || position > getEngineEndTime() || correctPosition > getEngineDuration()) {
            return RESULT_FAILED;
        }
        isInputEOF = false;
        isOutputEOF = false;
        if (position == getPosition()) {
            peekFrame().setValid(true);
            return RESULT_OK;
        }
        if (position < getPosition()) {

            mediaExtractor.seekTo(correctPosition + getClipStartTime(), MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
            mediaCodec.flush();
            peekFrame().setPts(Long.MIN_VALUE);
        }
        while (peekFrame().getPts() < position) {
            readFrame();
        }
        setPosition(position);
        return RESULT_OK;
    }

    @Override
    public String toString() {
        return "AVVideo{\n" +
                "\tisInputEOF=" + isInputEOF + ",\n" +
                "\tisOutputEOF=" + isOutputEOF + ",\n" +
                "\tpath='" + path + '\'' + ",\n" +
                "\tmediaCodec=" + mediaCodec + ",\n" +
                "\tmediaExtractor=" + mediaExtractor + ",\n" +
                "\tgop=" + gop + ",\n" +
                "\tmediaFormat=" + mediaFormat + ",\n" +
                "\tsurface=" + surface + ",\n" +
                "\tsurfaceTexture=" + surfaceTexture + ",\n" +
                "\tvideoSize=" + videoSize + ",\n" +
                "\tisTextureType=" + isTextureType + ",\n" +
                "\tframeRate=" + frameRate + ",\n" +
                "} " + super.toString() + "\n";
    }
}
